package org.convallaria.system.biz.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.system.biz.service.SecurityAuditService;
import org.convallaria.system.biz.service.sys.SysUserService;
import org.convallaria.system.biz.service.TokenBlacklistService;
import org.convallaria.system.biz.service.UserOnlineService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 用户在线状态管理服务实现
 * 
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserOnlineServiceImpl implements UserOnlineService {

    private final RedisTemplate<String, Object> redisTemplate;
    private final SysUserService sysUserService;
    private final SecurityAuditService securityAuditService;
    private final TokenBlacklistService tokenBlacklistService;

    // Redis键前缀
    private static final String USER_ONLINE_PREFIX = "user:online:";
    private static final String USER_SESSION_PREFIX = "user:session:";
    private static final String ONLINE_USERS_KEY = "online:users";
    private static final String USER_ACTIVITY_PREFIX = "user:activity:";

    // 会话超时时间（24小时）
    private static final Duration SESSION_TIMEOUT = Duration.ofHours(24);
    // 活动超时时间（30分钟）
    private static final Duration ACTIVITY_TIMEOUT = Duration.ofMinutes(30);
    // 日期时间格式化器
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public boolean isUserOnline(Long userId) {
        if (userId == null) {
            return false;
        }

        try {
            String key = USER_ONLINE_PREFIX + userId;
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("检查用户在线状态失败: userId={}", userId, e);
            return false;
        }
    }

    @Override
    public void setUserOnline(Long userId, String sessionId, String loginIp, String userAgent, LocalDateTime loginTime) {
        if (userId == null || sessionId == null) {
            log.warn("设置用户在线状态失败: 参数为空");
            return;
        }

        try {
            // 1. 设置用户在线状态
            String onlineKey = USER_ONLINE_PREFIX + userId;
            redisTemplate.opsForValue().set(onlineKey, sessionId, SESSION_TIMEOUT);

            // 2. 设置会话详细信息
            String sessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
            Map<String, Object> sessionInfo = new HashMap<>();
            sessionInfo.put("sessionId", sessionId);
            sessionInfo.put("loginIp", loginIp);
            sessionInfo.put("userAgent", userAgent);
            sessionInfo.put("loginTime", loginTime.format(DATE_TIME_FORMATTER));
            sessionInfo.put("lastActivity", LocalDateTime.now().format(DATE_TIME_FORMATTER));
            sessionInfo.put("userId", userId);

            redisTemplate.opsForHash().putAll(sessionKey, sessionInfo);
            redisTemplate.expire(sessionKey, SESSION_TIMEOUT);

            // 3. 添加到在线用户集合
            redisTemplate.opsForSet().add(ONLINE_USERS_KEY, userId);
            redisTemplate.expire(ONLINE_USERS_KEY, SESSION_TIMEOUT);

            // 4. 设置活动时间
            String activityKey = USER_ACTIVITY_PREFIX + userId;
            redisTemplate.opsForValue().set(activityKey, LocalDateTime.now().format(DATE_TIME_FORMATTER), ACTIVITY_TIMEOUT);

            log.info("用户上线成功: userId={}, sessionId={}, loginIp={}", userId, sessionId, loginIp);
        } catch (Exception e) {
            log.error("设置用户在线状态失败: userId={}, sessionId={}", userId, sessionId, e);
        }
    }

    @Override
    public void setUserOffline(Long userId, String sessionId) {
        if (userId == null) {
            return;
        }

        try {
            // 1. 删除用户在线状态
            String onlineKey = USER_ONLINE_PREFIX + userId;
            redisTemplate.delete(onlineKey);

            // 2. 删除会话信息
            if (sessionId != null) {
                String sessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
                redisTemplate.delete(sessionKey);
            }

            // 3. 从在线用户集合中移除
            redisTemplate.opsForSet().remove(ONLINE_USERS_KEY, userId);

            // 4. 删除活动时间
            String activityKey = USER_ACTIVITY_PREFIX + userId;
            redisTemplate.delete(activityKey);

            log.info("用户下线成功: userId={}, sessionId={}", userId, sessionId);
        } catch (Exception e) {
            log.error("设置用户离线状态失败: userId={}, sessionId={}", userId, sessionId, e);
        }
    }

    @Override
    public List<Map<String, Object>> getOnlineUsers(Integer pageNum, Integer pageSize) {
        try {
            // 获取所有在线用户ID
            Set<Object> onlineUserIds = redisTemplate.opsForSet().members(ONLINE_USERS_KEY);
            if (onlineUserIds == null || onlineUserIds.isEmpty()) {
                return new ArrayList<>();
            }

            List<Map<String, Object>> onlineUsers = new ArrayList<>();
            for (Object userIdObj : onlineUserIds) {
                Long userId = Long.valueOf(userIdObj.toString());
                Map<String, Object> userInfo = getUserSessionInfo(userId);
                if (userInfo != null) {
                    // 获取用户基本信息
                    var user = sysUserService.getById(userId);
                    if (user != null) {
                        userInfo.put("userName", user.getUserName());
                        userInfo.put("nickName", user.getNickName());
                        userInfo.put("email", user.getEmail());
                        userInfo.put("phone", user.getPhoneNumber());
                    }
                    onlineUsers.add(userInfo);
                }
            }

            // 分页处理
            int start = (pageNum - 1) * pageSize;
            int end = Math.min(start + pageSize, onlineUsers.size());
            if (start >= onlineUsers.size()) {
                return new ArrayList<>();
            }

            return onlineUsers.subList(start, end);
        } catch (Exception e) {
            log.error("获取在线用户列表失败", e);
            return new ArrayList<>();
        }
    }

    @Override
    public long getOnlineUserCount() {
        try {
            Long count = redisTemplate.opsForSet().size(ONLINE_USERS_KEY);
            return count != null ? count : 0L;
        } catch (Exception e) {
            log.error("获取在线用户总数失败", e);
            return 0L;
        }
    }

    @Override
    public boolean forceUserOffline(Long userId, String reason) {
        if (userId == null) {
            return false;
        }

        try {
            // 1. 获取用户当前会话ID
            String onlineKey = USER_ONLINE_PREFIX + userId;
            String sessionId = (String) redisTemplate.opsForValue().get(onlineKey);

            if (sessionId != null) {
                // 2. 将Token加入黑名单
                tokenBlacklistService.addToBlacklist(sessionId, reason != null ? reason : "管理员强制下线");

                // 3. 设置用户离线
                setUserOffline(userId, sessionId);

                // 4. 记录审计日志
                securityAuditService.recordSensitiveOperationEvent(userId, "FORCE_LOGOUT", "用户强制下线", 
                    "管理员强制下线: " + (reason != null ? reason : "无原因"));

                log.info("强制用户下线成功: userId={}, sessionId={}, reason={}", userId, sessionId, reason);
                return true;
            } else {
                log.warn("用户不在线，无法强制下线: userId={}", userId);
                return false;
            }
        } catch (Exception e) {
            log.error("强制用户下线失败: userId={}", userId, e);
            return false;
        }
    }

    @Override
    public boolean isSessionValid(Long userId, String sessionId) {
        if (userId == null || sessionId == null) {
            return false;
        }

        try {
            // 1. 检查用户是否在线
            if (!isUserOnline(userId)) {
                return false;
            }

            // 2. 检查会话是否存在
            String sessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
            return Boolean.TRUE.equals(redisTemplate.hasKey(sessionKey));
        } catch (Exception e) {
            log.error("检查会话有效性失败: userId={}, sessionId={}", userId, sessionId, e);
            return false;
        }
    }

    @Override
    public void updateLastActivity(Long userId, String sessionId) {
        if (userId == null) {
            return;
        }

        try {
            // 更新活动时间
            String activityKey = USER_ACTIVITY_PREFIX + userId;
            redisTemplate.opsForValue().set(activityKey, LocalDateTime.now().format(DATE_TIME_FORMATTER), ACTIVITY_TIMEOUT);

            // 更新会话中的最后活动时间
            if (sessionId != null) {
                String sessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
                redisTemplate.opsForHash().put(sessionKey, "lastActivity", LocalDateTime.now().format(DATE_TIME_FORMATTER));
            }
        } catch (Exception e) {
            log.error("更新用户活动时间失败: userId={}", userId, e);
        }
    }

    @Override
    public int cleanExpiredSessions() {
        int cleanedCount = 0;
        try {
            // 获取所有在线用户
            Set<Object> onlineUserIds = redisTemplate.opsForSet().members(ONLINE_USERS_KEY);
            if (onlineUserIds == null || onlineUserIds.isEmpty()) {
                return 0;
            }

            LocalDateTime now = LocalDateTime.now();
            List<Long> expiredUsers = new ArrayList<>();

            for (Object userIdObj : onlineUserIds) {
                Long userId = Long.valueOf(userIdObj.toString());
                String activityKey = USER_ACTIVITY_PREFIX + userId;
                Object lastActivityObj = redisTemplate.opsForValue().get(activityKey);

                if (lastActivityObj == null) {
                    // 活动时间不存在，认为已过期
                    expiredUsers.add(userId);
                } else {
                    try {
                        // 解析字符串格式的时间
                        String lastActivityStr = lastActivityObj.toString();
                        LocalDateTime lastActivity = LocalDateTime.parse(lastActivityStr, DATE_TIME_FORMATTER);
                        if (lastActivity.isBefore(now.minus(ACTIVITY_TIMEOUT))) {
                            expiredUsers.add(userId);
                        }
                    } catch (Exception e) {
                        // 解析失败，认为已过期
                        log.warn("解析用户活动时间失败: userId={}, lastActivity={}", userId, lastActivityObj, e);
                        expiredUsers.add(userId);
                    }
                }
            }

            // 清理过期用户
            for (Long userId : expiredUsers) {
                setUserOffline(userId, null);
                cleanedCount++;
            }

            log.info("清理过期会话完成: 清理数量={}", cleanedCount);
        } catch (Exception e) {
            log.error("清理过期会话失败", e);
        }

        return cleanedCount;
    }

    @Override
    public Map<String, Object> getUserSessionInfo(Long userId) {
        if (userId == null) {
            return null;
        }

        try {
            String onlineKey = USER_ONLINE_PREFIX + userId;
            String sessionId = (String) redisTemplate.opsForValue().get(onlineKey);

            if (sessionId == null) {
                return null;
            }

            String sessionKey = USER_SESSION_PREFIX + userId + ":" + sessionId;
            Map<Object, Object> sessionData = redisTemplate.opsForHash().entries(sessionKey);

            if (sessionData.isEmpty()) {
                return null;
            }

            Map<String, Object> sessionInfo = new HashMap<>();
            sessionData.forEach((key, value) -> sessionInfo.put(key.toString(), value));

            return sessionInfo;
        } catch (Exception e) {
            log.error("获取用户会话信息失败: userId={}", userId, e);
            return null;
        }
    }
}
